Диаграмма с параллельными координатами

Диаграмма parallel (параллельных координат) позволяет визуализировать значения по нескольким признакам (измерениям, переменным) для набора объектов. Каждый признак представлен своей вертикальной или горизонтальной осью, а каждая линия (полилиния) соединяет значения для одного объекта или группы во всех осях.
Области применения:
  • Анализ многомерных данных: профили пользователей, товаров, производственных партий, сравнение характеристик конкурентов и др.
  • Поиск схожих и аномальных объектов, выявление кластеров и outliers
  • Визуализация динамики и структуры признаков для разных категорий
Структура данных:
  1. parallelAxis — массив объектов, каждый из которых задаёт одну ось (название, тип и пр.).
  2. series.data — массив массивов или объектов, где каждая вложенная последовательность содержит значения для всех осей.
Для примера рассмотрим изменение прибыли по годам для различных категорий продуктов.
Оператор данных:
function dataCal(originData) {
  var dataModels = originData.dataModels;
  var colData = dataModels[0].colData;
  var data = [];
  for (var i = 0; i < colData[0].length; i++) {
    data[i] = {
      year: colData[0][i],
      product: colData[1][i],
      value: colData[2][i]
    };
  }
  return data;
}
Оператор конфигураций:
function optionCal(data, chartConfig, chart) {
  var years = [];
  var products = [];
  data.forEach(function(item) {
    if (years.indexOf(item.year) === -1) years.push(item.year);
    if (products.indexOf(item.product) === -1) products.push(item.product);
  });

  // Формируем массив для каждой линии: [value1, value2, ...]
  var seriesData = products.map(function(product) {
    return years.map(function(year) {
      var found = data.find(function(item) {
        return item.product === product && item.year === year;
      });
      return found ? Number(found.value) : null;
    });
  });

  // Параллельные оси — года
  var parallelAxis = years.map(function(year, idx) {
    return { dim: idx, name: String(year), type: 'value' };
  });

  var colorPalette = [
    "#5470C6", "#91CC75", "#EE6666", "#FAC858", "#73C0DE",
    "#3BA272", "#FC8452", "#9A60B4", "#EA7CCC"
  ];

  var lineColors = {};
  products.forEach(function(product, idx) {
    lineColors[product] = colorPalette[idx % colorPalette.length];
  });

  var option = {
    title: { text: 'Parallel Chart Example', left: 'center' },
    parallelAxis: parallelAxis,
    parallel: { left: 80, right: 50, bottom: 50, top: 60 },
    series: [{
      type: 'parallel',
      lineStyle: {
        width: 3,
        opacity: 0.7
      },
      data: seriesData.map(function(values, idx) {
        return {
          value: values,
          lineStyle: {
            color: colorPalette[idx % colorPalette.length],
            width: 3,
            opacity: 0.8
          }
        };
      })
    }],
    legend: {
      data: products,
      left: 0,
      top: 60,
      orient: 'vertical',
      textStyle: { fontSize: 14 }
    },
    tooltip: {
      trigger: 'item',
      formatter: function(params) {
        var productIdx = params.dataIndex;
        var product = products[productIdx];
        var details = years.map(function(year, idx) {
          var val = params.value[idx];
          return '<br/>' + year + ': ' + (val != null ? Math.round(val) : '—');
        }).join('');
        return 'Продукт: ' + product + details;
      }
    }
  };
  return option;
}